/*
    Copyright (C) 2003  Brano Kemen

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "coid/comm/commtypes.h"
#include "coid/comm/str.h"

#ifdef SYSTYPE_MSVC


#define WINVER 0x0500
#include <windows.h>
#include <process.h>
#include <stdio.h>
#include <direct.h>
#include "resource.h"
#include "coid/comm/dir.h"

LRESULT CALLBACK coidexe_wndproc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

HMENU Gmenu;
int Gport=0;

static char* GCOID_cmdline=0;

#define COIDD       "coidd.exe"
#define COID        "coid.exe"
#define COIDCMDCD   "coidcd.exe"
#define COIDCMDWD   "coidcwd.exe"
#define COIDCMDC    "coidc.exe"
#define COIDCMDW    "coidcw.exe"


#include "coid/comm/str.h"
using namespace coid;

typedef int (* fn_coid_server_start)( int& port, int (*cancel_cbk)(int) );
typedef int (* fn_coid_server_stop)();
typedef int (* fn_coid_get_version)( charstr & );


const char * g_look_in[] = {"."};
int g_look_in_count = sizeof(g_look_in) / sizeof(char *);


////////////////////////////////////////////////////////////////////////////////
int cancel_request( int restart )
{
    if( restart )
    {
        PROCESS_INFORMATION pi;

        if( !CreateProcess( COIDD, GCOID_cmdline, 0, 0, false,
            CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP, 0, 0, 0, &pi ) )
            CreateProcess( COID, GCOID_cmdline, 0, 0, false,
            CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP, 0, 0, 0, &pi );
    }

    PostQuitMessage(0);
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
int usage()
{
    MessageBox( NULL, "Usage: coid [-v | --version] [-p port] [config_directory]", NULL, MB_OK );
    return 1;
}

////////////////////////////////////////////////////////////////////////////////
void print_msgbox( const char * fs, const char * s=NULL, bool info=false, const char * title=NULL )
{
	char tmp[1024];
	const char * p = tmp;
	if( s ) sprintf( tmp, fs, s );
	else p = fs;
	MessageBox( NULL, p, title, MB_OK | (info ? MB_ICONINFORMATION : 0) );
}

////////////////////////////////////////////////////////////////////////////////
int CALLBACK WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
    GCOID_cmdline = lpCmdLine;

	bool bonly_print_version = false;
    const char * working_dir = NULL;
    charstr lib_name;
    charstr tmp, tmp2;
	token tok( lpCmdLine );

    while( !tok.is_empty() ) {
        token tok2 = tok.cut_left(" \t", token::cut_trait_remove_all());
        if( tok2[0] == '-' ) {
            ++tok2;
			if( tok2[0] == '-' ) {
                ++tok2;
				if( tok2 == "version" )
					bonly_print_version = true;
				else if( tok2 == "help" )
					return usage();
			}
            else if( tok2[0] == 'v' ) bonly_print_version = true;
            else if( tok2[0] == 'l' ) {
                lib_name = tok.cut_left(" \t", token::cut_trait_remove_all());
            }
            else if( tok2[0] == 'p' ) {
				token tok3 = tok.cut_left(" \t", token::cut_trait_remove_all());
                Gport = tok3.touint();
            }
        }
        else {
			tmp2 = tok2;
            if( _chdir(tmp2.ptr()) != 0 ) {
                print_msgbox( "Invalid directory: '%s'\n", tmp2.c_str() ); 
                return usage();
            }
            else {
                working_dir = tmp2.ptr();
                uints len = strlen( working_dir );
                if( len && working_dir[len-1] == '\\' || working_dir[len-1] == '/' )	// '/'
                    (char &) working_dir[len-1] = 0;
            }
        }
    }

    if(!Gport)
        Gport = 55099;


    sysDynamicLibrary server_lib;
    const char ** p = g_look_in;
    bool bfound = false;
    if( lib_name.is_empty() )
#ifdef _DEBUG
# ifdef SYSTYPE_64
        lib_name = "libcoidserver64d.dll";
# else
        lib_name = "libcoidserverd.dll";
# endif
#else
# ifdef SYSTYPE_64
        lib_name = "libcoidserver64.dll";
# else
        lib_name = "libcoidserver.dll";
# endif
#endif

    server_lib.open( lib_name.c_str() );

    if( !server_lib.is_valid() ) {
        if( bfound ) print_msgbox( "Cannot open server library %s\n", tmp.ptr() );
        else print_msgbox( "Cannot find server library\n" );
        return 2;
    }
    //printf( "Using server library %s\n", (char *) tmp ); 

	if( bonly_print_version ) {
		void * fnc = server_lib.getFuncAddress( "coid_get_version" );
		if( ! fnc ) {
			print_msgbox( "Error loading server library %s\n", tmp.ptr() );
			return 3;
		}

	    fn_coid_get_version coid_get_version = (fn_coid_get_version) fnc;
		charstr ver;
		coid_get_version( ver );
		print_msgbox( "%s\n", ver.ptr(), true, "Version info" );
		return 0;
	}

    void * fnc1 = server_lib.getFuncAddress( "coid_server_start" );
    if( ! fnc1 ) {
        print_msgbox( "Error loading server library %s\n", tmp.ptr() );
        return 3;
    }

    void * fnc2 = server_lib.getFuncAddress( "coid_server_stop" );
    if( ! fnc2 ) {
        print_msgbox( "Error loading server library %s\n", tmp.ptr() );
        return 3;
    }


    fn_coid_server_start coid_server_start = (fn_coid_server_start) fnc1;
    fn_coid_server_stop coid_server_stop = (fn_coid_server_stop) fnc2;


    if( 0 != coid_server_start( Gport, &cancel_request ) ) {
        coid_server_stop();
        return -1;
    }

    WNDCLASS wc;
    memset (&wc, 0, sizeof(wc));
    wc.lpfnWndProc = coidexe_wndproc;
    wc.hInstance = GetModuleHandle (0);
    wc.hIcon = LoadIcon (wc.hInstance, MAKEINTRESOURCE(IDI_COID));
    wc.lpszClassName = "coid_wndcls";

    RegisterClass (&wc);
    HWND hwnd = CreateWindowEx (0, "coid_wndcls", "coid", 0,
        0, 0, 16, 16,
        HWND_MESSAGE, 0, 0, 0);

    NOTIFYICONDATA nid;
    nid.cbSize = sizeof(nid);
    nid.hWnd = hwnd;
    nid.uID = 0;
    nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
    nid.uCallbackMessage = WM_USER;

    strcpy( nid.szTip, "COID framework (port " );

    char puf[8];
    strcat( nid.szTip, _itoa(Gport, puf, 10) );
    strcat( nid.szTip, ")" );
    nid.hIcon = wc.hIcon;

    Shell_NotifyIcon (NIM_ADD, &nid);
    DestroyIcon (wc.hIcon);

    Gmenu = CreatePopupMenu();

    MENUITEMINFO mif;
    mif.cbSize = sizeof (mif);
    mif.fMask = MIIM_TYPE | MIIM_ID;
    mif.fType = MFT_STRING;
    mif.fState = 0;
    mif.wID = 888;
    mif.dwTypeData = "Shutdown";
    InsertMenuItem (Gmenu, 0, true, &mif);
/*
    mif.fType = MFT_SEPARATOR;
    InsertMenuItem (Gmenu, 0, true, &mif);

    mif.fType = MFT_STRING;
    mif.dwTypeData = "Cmd console";
    mif.wID = 889;
    InsertMenuItem (Gmenu, 0, true, &mif);

    mif.dwTypeData = "Cmd console -all";
    mif.wID = 890;
    InsertMenuItem (Gmenu, 0, true, &mif);

    mif.fType = MFT_STRING;
    mif.dwTypeData = "Cmd window";
    mif.wID = 891;
    InsertMenuItem (Gmenu, 0, true, &mif);

    mif.dwTypeData = "Cmd window -all";
    mif.wID = 892;
    InsertMenuItem (Gmenu, 0, true, &mif);
*/

    MSG msg;
    while(1)
    {
        if( !GetMessage( &msg, 0, 0U, 0U ) )
            break;

        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }

    DestroyMenu(Gmenu);
    Shell_NotifyIcon( NIM_DELETE, &nid );

    coid_server_stop();
//    ExitProcess(0);

    return -1;
}

////////////////////////////////////////////////////////////////////////////////
void popup (HWND hwnd, long x, long y)
{
    SetForegroundWindow(hwnd);

    long r = TrackPopupMenu (Gmenu,
        TPM_RIGHTALIGN | TPM_BOTTOMALIGN | TPM_NONOTIFY | TPM_RETURNCMD | TPM_LEFTBUTTON,
        x, y, 0, hwnd, 0);

    if(r == 888)
        PostQuitMessage(0);
/*    else if( r >= 889 && r <= 892 )
    {
        //system ("coidcmdc.exe");
        //this seems to disable network for coidexe if it's not killed alongwith it
        //_flushall ();
        //spawnl (_P_NOWAIT, "coidcmdc.exe", "coidcmdc.exe", NULL);

        charstr consoletitle;
        consoletitle = Gport;
        consoletitle += " COID remote console";

        STARTUPINFO si;
        memset (&si, 0, sizeof(si));
        si.cb = sizeof (si);
        si.lpTitle = (char*) consoletitle.ptr();
        si.dwXCountChars = 100;
        si.dwYCountChars = 8192;
        si.dwXSize = 100*8;
        si.dwYSize = 28*12;
        si.dwFlags = STARTF_USECOUNTCHARS | STARTF_USESIZE;

        charstr cmdl = r > 890 ? COIDCMDW : COIDCMDC;
        cmdl << " localhost:" << Gport;

        if( r == 890 || r == 892 )
            cmdl << " -all";

        PROCESS_INFORMATION pi;
        if( !CreateProcess (r > 890 ? COIDCMDWD : COIDCMDCD, (char*)cmdl.ptr(), 0, 0, false,
            CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, 0, 0, &si, &pi)
            )
            CreateProcess (r > 890 ? COIDCMDW : COIDCMDC, (char*)cmdl.ptr(), 0, 0, false,
            CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, 0, 0, &si, &pi);
    }*/
}

////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK coidexe_wndproc (
  HWND hwnd,
  UINT uMsg,
  WPARAM wParam,
  LPARAM lParam
)
{
    switch (uMsg)
    {
    case WM_USER:
        if (lParam == WM_RBUTTONUP)
        {
            POINT p;
            GetCursorPos (&p);
            popup (hwnd, p.x, p.y);
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    };

    return DefWindowProc (hwnd, uMsg, wParam, lParam);
}



#endif //SYSTYPE_MSVC
